﻿
using Controller.Enum;
using Controller.Model;
using Controller.Utils;
using Leeder.Logger;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DPS.ControllerClient
{
    /// <summary>
    /// 控制器的客户端通讯处理
    /// </summary>
    public class BaseController
    {
        #region 变量声明

        /// <summary>
        /// 日志对象
        /// </summary>
        private LogOperate LogObj;
        /// <summary>
        /// 通讯间隔时间
        /// </summary>
        private const int INTERVAL_TIME = 20;
        /// <summary>
        /// 控制器服务的IP
        /// </summary>
        private string controllerIp;
        /// <summary>
        /// 控制器服务的端口
        /// </summary>
        private int controllerPort;
        /// <summary>
        /// 控制器是否连接
        /// </summary>
        private bool isConnection;
        /// <summary>
        /// 服务是否停止
        /// </summary>
        private bool isStop;
        /// <summary>
        /// 通讯线程
        /// </summary>
        private Thread communicationThread;
        /// <summary>
        /// 远端Socket
        /// </summary>
        private Socket remoteSocket;
        /// <summary>
        /// 发送数据的队列
        /// </summary>
        private ConcurrentQueue<ServerPacketModel> sendQueue;

        #endregion  变量声明

        #region 事件定义

        /// <summary>
        /// 设备异常事件,参数1:ip:port、参数2:设备地址、参数3:控制器异常代码
        /// </summary>
        internal Action<string, string, ControllerExceptionCodeEnum> OnDeviceExceptionEvent;
        /// <summary>
        /// 触发设备按钮事件
        /// </summary>
        internal Action<TriggerLabelPacketModel> OnDeviceButtonEvent;
        /// <summary>
        /// 设备状态事件
        /// </summary>
        internal Action<LabelStatePacketModel> OnDeviceStateEvent;
        /// <summary>
        /// 控制器连接状态修改事件
        /// </summary>
        internal Action<ControllerStatePacketModel> OnConnectionStateChangeEvent;
        /// <summary>
        /// 控制器连接管理状态事件
        /// </summary>
        internal Action<bool> OnControllerManagerStateEvent;

        #endregion 事件定义

        #region 构造函数

        internal BaseController(string iIp, int iPort) {
            this.controllerIp = iIp;
            this.controllerPort = iPort;
        }

        #endregion 构造函数

        /// <summary>
        /// 启动
        /// </summary>
        internal void Start() {
            LogObj = Log.GetLogger(GetType());
            sendQueue = new ConcurrentQueue<ServerPacketModel>();
            isStop = false;
            isConnection = false;

            communicationThread = new Thread(CommunicationFunction) { IsBackground = true };
            communicationThread.Start();
        }

        /// <summary>
        /// 结束
        /// </summary>
        internal void Stop() {
            isStop = true;
            Thread.Sleep(5 * INTERVAL_TIME);
            if (communicationThread != null)
            {
                try { communicationThread.Abort(); } catch { }
                communicationThread = null;
            }
        }

        /// <summary>
        /// 通讯函数
        /// </summary>
        private void CommunicationFunction() {
            //Connection();
            while (!isStop) {
                try
                {
                    if (!isConnection) {
                        if (!Connection()) { 
                            Thread.Sleep(50);
                            continue;
                        }
                    }
                    //if (sendQueue == null || sendQueue.IsEmpty)
                    //{
                    //    Thread.Sleep(INTERVAL_TIME);
                    //}
                    SocketSend(GetSendData());// 发送数据
                    UpdateConnectionState(true);// 更新连接状态
                    ServerPacketModel packet = ReceiveData();// 接收数据
                    // 过滤掉心跳包
                    if (packet.CmdType == CommunicationCmdTypeEnum.HEART_BEAT)
                    {
                        Thread.Sleep(INTERVAL_TIME);
                        continue;
                    }
                    ReceiveDataHandle(packet);
                    UpdateConnectionState(true);
                }
                catch (Exception ex)
                {
                    try { remoteSocket.Close(); } catch { }
                    UpdateConnectionState(false);
                    LogObj.Error(ExceptionType.BusinessException, ex);
                    Thread.Sleep(500);
                }
            }
        }

        /// <summary>
        /// 接收数据处理
        /// </summary>
        /// <param name="iPacket"></param>
        private void ReceiveDataHandle(ServerPacketModel iPacket) {
            // 接收事件处理 -- 标签返回状态、标签异常、标签反馈
            switch (iPacket.CmdType)
            {
                case CommunicationCmdTypeEnum.RETURN_EXCEPTION:
                    OnDeviceExceptionEvent?.BeginInvoke(iPacket.ControllerKey,
                        ((ExceptionInfoPacketModel)iPacket).Address, ((ExceptionInfoPacketModel)iPacket).ExceptionCode, null, null);
                    break;
                case CommunicationCmdTypeEnum.RETURN_LABEL_STATE:
                    OnDeviceStateEvent?.BeginInvoke((LabelStatePacketModel)iPacket, null, null);
                    break;
                case CommunicationCmdTypeEnum.RETURN_SINGLE:
                    OnDeviceButtonEvent?.BeginInvoke((TriggerLabelPacketModel)iPacket, null, null);
                    LogObj.Debug($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 接收到反馈信息!\r\n");
                    break;
                case CommunicationCmdTypeEnum.CONTROLLER_STATE:
                    OnConnectionStateChangeEvent?.BeginInvoke((ControllerStatePacketModel)iPacket, null, null);
                    break;
                default:
                    LogObj.Error($"返回类型[{iPacket.CmdType}]无效!{ByteUtils.ByteArrayToHexString(iPacket.Serialize())}\r\n");
                    break;
            }
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <returns></returns>
        private ServerPacketModel ReceiveData() {
            List<byte> dataPacket = new List<byte>();
            // 获取数据头
            byte[] buffer = SocketReceive(ServerPacketModel.PACKET_HEAT_LENGTH);
            // 如果读到的数据帧头不匹配则接着读取，直到读到正确的数据帧头为止
            while (buffer[0] != ServerPacketModel.HEAT) {
                buffer = SocketReceive(ServerPacketModel.PACKET_HEAT_LENGTH);
            }
            dataPacket.AddRange(buffer);
            // 先读取数据头到命令体长度的数据
            buffer = SocketReceive(ServerPacketModel.PACKET_BASE_LENGTH - ServerPacketModel.PACKET_TAIL_LENGTH - ServerPacketModel.PACKET_HEAT_LENGTH);
            if (buffer[0] == 4 || buffer[1] == 4) {
                Console.WriteLine();
            }
            dataPacket.AddRange(buffer);
            buffer = dataPacket.ToArray();
            CommunicationCmdTypeEnum type = (CommunicationCmdTypeEnum)BitConverter.ToUInt16(buffer, ServerPacketModel.PACKET_CMD_TYPE_START_POSITION);
            UInt16 bodyLength = BitConverter.ToUInt16(buffer, ServerPacketModel.PACKET_BODY_LENGTH_START_POSITION);
            // 接收数据包体和数据帧尾
            buffer = SocketReceive(bodyLength + ServerPacketModel.PACKET_TAIL_LENGTH);
            dataPacket.AddRange(buffer);
            ServerPacketModel packet = GetServerPacketModelByType(type);
            packet.UnSerialize(dataPacket.ToArray());
            return packet;
        }

        /// <summary>
        /// 获取发送数据
        /// </summary>
        /// <returns></returns>
        private byte[] GetSendData() {
            ServerPacketModel packet;
            // 队列为空或者从队列中取数据失败
            if (sendQueue.IsEmpty || !sendQueue.TryDequeue(out packet)) {
                packet = new ServerPacketModel { CmdType = CommunicationCmdTypeEnum.HEART_BEAT };
            }
            
            return packet.Serialize();
        }

        /// <summary>
        /// 连接
        /// </summary>
        private bool Connection()
        {
            try
            {
                remoteSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                //remoteSocket.ReceiveTimeout = 3000;
                remoteSocket.Connect(controllerIp, controllerPort);
                UpdateConnectionState(true);
            }
            catch (Exception ex)
            {
                LogObj.Error(ExceptionType.BusinessException, ex, "connection error\r\n");
                UpdateConnectionState(false);
                return false;
            }
            return true;
        }

        /// <summary>
        /// socket发送数据
        /// </summary>
        private void SocketSend(byte[] data)
        {
            int length = data.Length, position = 0;
            while (position < length)
            {
                position += remoteSocket.Send(data, position, length - position, SocketFlags.None);
            }
        }

        /// <summary>
        /// socket接收数据
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        private byte[] SocketReceive(int length)
        {
            int position = 0;
            byte[] data = new byte[length];
            while (position < length)
            {
                position += remoteSocket.Receive(data, position, length - position, SocketFlags.None);
            }
            return data;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private ServerPacketModel GetServerPacketModelByType(CommunicationCmdTypeEnum type) {
            switch (type) {
                case CommunicationCmdTypeEnum.ACTION:
                    return new ActionPacketModel();
                case CommunicationCmdTypeEnum.CANCEL:
                    return new CancelPacketModel();
                case CommunicationCmdTypeEnum.GET_LABEL_STATE:
                    return new GetLabelStatePacketModel();
                case CommunicationCmdTypeEnum.RETURN_EXCEPTION:
                    return new ExceptionInfoPacketModel();
                case CommunicationCmdTypeEnum.RETURN_LABEL_STATE:
                    return new LabelStatePacketModel();
                case CommunicationCmdTypeEnum.RETURN_SINGLE:
                    return new TriggerLabelPacketModel();
                case CommunicationCmdTypeEnum.CONTROLLER_STATE:
                    return new ControllerStatePacketModel();
                case CommunicationCmdTypeEnum.REGISTRATION_DEVICE:
                    return new RegistrationDevicePacketModel();
                case CommunicationCmdTypeEnum.GET_LABEL_VOLTAGE:
                    return new GetDeviceVoltagePacketModel();
                case CommunicationCmdTypeEnum.DISPLAY_DATA:
                    return new DisplayDataPacketModel();
                case CommunicationCmdTypeEnum.VIBRATION:
                    return new VibrationPacketModel();
                case CommunicationCmdTypeEnum.WRITE_RFID:
                    return new WriteRfidPacketModel();
                case CommunicationCmdTypeEnum.SETTING_PANID_CHANNEL:
                    return new SettingPanIdChannelPacketModel();
            }
            return new ServerPacketModel();
        }

        /// <summary>
        /// 设置发送数据,把数据放到发送队列中
        /// </summary>
        /// <param name="iModel"></param>
        internal void SetSendData(ServerPacketModel iModel)
        {
            sendQueue.Enqueue(iModel);
        }

        /// <summary>
        /// 下发控制命令
        /// </summary>
        /// <param name="iPacket"></param>
        public void Control(ServerPacketModel iPacket)
        {
            SetSendData(iPacket);
        }

        /// <summary>
        /// 更新连接状态
        /// </summary>
        /// <param name="iState"></param>
        private void UpdateConnectionState(bool iState) {
            // 当前状态没有修改
            if (isConnection == iState) return;
            isConnection = iState;
            OnControllerManagerStateEvent?.BeginInvoke(isConnection, null, null);
        }
    }
}
